<?php

class commnet {

	const VERSION = '0.0prelawl';
	static $VERSION;

	static $configfile = null;
	static $network = array();
	static $colour = array();
	
	static $currentChannel = null;

	public $stdout;

}

class ircEvent {

	const MESSAGE = 1;
	const NOTICE = 2;
	const QUERY = 4;
	const NICK = 8;
	const PING = 16;
	const QUIT = 32;
	const CONNECT = 64;
	const JOIN = 128;	
	const PART = 256;
	const MODE = 512;
	const KICK = 1024;
	const BAN = 2048;
	const INVITE = 4096;
	const HEARTBEAT = 8192;
	const ACTION = 16384;
	const CTCP = 32768;

	public $admin;

	public function __construct($type,$network,$data) {
	
		$this->type = $type;
		$this->network = $network;
		
		if(is_array($data)) {
			foreach($data as $property => $value) {
				$this->$property = $value;
			}
		}
	
		return;
	}
	
	static function runScripts($event) {
		$dp = opendir(EXECROOT.'/usr/share/commnet/scripts');
		while($file = readdir($dp)) {
			if(substr($file,-4,4) == '.php') {
				self::runScript(EXECROOT."/usr/share/commnet/scripts/{$file}",$event);
			}
		} closedir($dp);
	}
	
	static function runScript($file,$event) {
		include($file);
	}
	
	static function runCommand($text) {
		$text = ltrim($text,'/');
		$command = strtolower( substr($text,0,($pos = strpos($text,chr(32))) ));
		$value = substr($text,$pos + 1,(strlen($text) - $pos));
		if($pos === false) {
			$command = $text;
			$value = '';
		}
		
		if(lcars::$w->currentChannel) {
			$network = lcars::$w->currentChannel->network;
			$channel = lcars::$w->currentChannel;
		} else {
			$network = $channel = null;
		}
		
		require(EXECROOT.'/usr/libexec/commnet/commnet-cmd.so.php');		
	
	}
}

class ircNetwork {

	const CONNECTED = 1;
	const PENDING = 2;
	const ONLINE = 4;

	public $button,$terminal;
	public $network,$server,$port,$autoconnect;
	public $nick,$altnick,$user,$info;
	public $channels = array();
	
	public $state,$whoami;
	public $channel;
	public $stream,$buffer,$iowatch,$reconTimer;
	
	public function __construct($network,$arg) {
		$this->network = $network;
		foreach($arg as $id => $value) {
			if(array_key_exists($id,$this)) {
				$this->$id = $value;
			}
		}
		
		$this->stream = null;
		$this->buffer = '';
		
		return;
	}
	
	public function create() {
		$this->createNullChannel();
		$this->button = lcars::$w->addNetworkButton($this);
		lcars::$w->sortNetworkList();
		return;
	}

	public function createNullChannel() {
		$this->channel['null'] = new ircChannel('null',$this,false);
		$this->channel['null']->terminal->append("Network: {$this->network}");
		return;
	}

	public function connect() {
		if($this->state & self::ONLINE) { return; }
		
		$this->state = 0;

		if($this->reconTimer) { Gtk::timeout_remove($this->reconTimer); }
		
		lcars::$w->text1->buffer->append(lcars::date()." Connecting to {$this->network}...");
		if($this->stream = fsockopen($this->server,$this->port)) {
			$this->state |= self::CONNECTED;
			stream_set_blocking($this->stream,false);
		} else {
			lcars::$w->text1->buffer->append(lcars::date()." Error connecting to {$this->network}... retry in 30 seconds.");
			$this->reconTimer = Gtk::timeout_add(30000,array($this,'connect'));
			return false;
		}

		lcars::$w->text1->buffer->append(lcars::date()." Logging in to {$this->network}...");
		$this->writeNetwork('NICK %s',$this->nick);
		$this->writeNetwork('USER %s %s %s :%s',$this->user,$this->user,$this->user,$this->info);
		$this->state |= self::PENDING;	
			
		$this->buffer = '';
		$this->iowatch = Gtk::io_add_watch($this->stream,GObject::IO_IN,array($this,'readNetwork'));
	
		return false; 
	}
	
	public function disconnect() {
		$this->writeNetwork('QUIT');
		Gtk::input_remove($this->iowatch);
		fclose($this->stream);
		$this->state = 0;
		lcars::$w->text1->buffer->append(lcars::date()." Disconnected from {$this->network}.");
		return;
	}
	
	public function writeNetwork($format,$spam = true,$stamp = true) {
		$arg = func_get_args();
		unset($arg[0]);
		
		$string = vsprintf($format,$arg);
		if($spam) {
			lcars::$w->text1->buffer->append(sprintf(
				'%s%s <= %s',
				(($stamp)?(lcars::date().' '):('')),
				$this->network,
				preg_replace("/\001/",'',$string)
			));
		}
		if($this->state & self::CONNECTED && $this->stream) {
			fwrite($this->stream,"$string\n");
		}
				
		return;
	}	
	
	public function readNetwork($stream,$cond) {
	
		//. stream set unblocking.
		//. my first idea about handling incoming data with glib iowatch.
		//. don't lol me if i did something stupid, its fkn 5am;
		
		while($recv = fgets($stream)) {
		
			$event = null;
		
			//. ok here is the deal. i like fgets. but it does cause problems
			//. if the stream does not end in \n and since all complete irc
			//. messages do, i like it. but it does cause problems if we run
			//. in blocking mode waiting for a \n, or in unblocking mode with
			//. incomplete data. this buffer implementation seems to have
			//. solved my immediate problems without rape. without blocking
			//. fgets is still able to fetch an entire line if it exists, and
			//. if not just returns the incomplete data which we can buffer.
			//. also since there could be more than one irc message in the
			//. buffer at one time this just naturally makes us read each
			//. statement.

			$this->buffer .= $recv;
			if(substr($this->buffer,-1,1) != chr(10)) {
				$this->buffer .= $recv;
				return true;
			}

			require(EXECROOT.'/usr/libexec/commnet/commnet-irc.so.php');
			
			$this->buffer = '';

		}

		return true;
	}
	
}

class ircChannel {

	public $terminal;
	public $channel;
	public $nicklist = array();
	public $mode;
	public $topic;
	public $button;

	public $network;

	public function __construct($channel,$network,$sendjoin = true) {
		$this->network = $network;
		$this->channel = $channel;
		if($sendjoin) {
			$this->network->writeNetwork("JOIN {$channel}");
		} else {
			$this->create();
		}

		return;
	}
	
	public function create() {
		$this->terminal = new lcarsTextBuffer;
		if($this->channel != 'null') {
			$this->terminal->append("CHANNEL BEGIN: {$this->channel}");
		}
		
		if($this->channel != 'null') {
			$this->button = lcars::$w->addChannelButton($this);
			lcars::$w->sortChannelList($this);
		}
		
		lcars::$w->setActiveChannel($this);
		return;
	}
	
	public function destroy() {
		if($this->channel == 'null') {
			$this->network->destroy();
			return;		
		}
		
		unset($this->terminal);
		$this->button->destroy();
	
		lcars::$w->nickBox["{$this->network->network}-{$this->channel}"]->destroy();		
		unset($this->network->channel[$this->channel]);
		return;
	}
	
	static function part() {
		if(is_object(lcars::$w->currentChannel) && lcars::$w->currentChannel->channel != 'null') {
			ircEvent::runCommand('part '.lcars::$w->currentChannel->channel);
		} else {
			ircEvent::runCommand('quit');
		}
		
		return;
	}

}

class ircNick {

	public $button;
	public $nick;
	public $mode;
	
	const NORMAL = 0;
	const VOICE = 1;
	const OP = 2;
	const HALFOP = 4;
	const ADMIN = 8;
	const OWNER = 16;
	
	public function __construct($nick,$mode,$button) {
		$this->nick = $nick;
		$this->mode = $mode;
		$this->button = $button;

		$button->setColour(self::colour($mode),true);
		
		return;
	}
	
	static public function updateNick($network) {
	
	}
	
	public function updateMode() {

		if($this->mode & self::OWNER) {
			$this->button->setColour(self::colour(self::OWNER),true);
			return;	
		}
		else if($this->mode & self::ADMIN) {
			$this->button->setColour(self::colour(self::ADMIN),true);
			return;		
		}
		else if($this->mode & self::OP) {
			$this->button->setColour(self::colour(self::OP),true);
			return;		
		}
		else if($this->mode & self::HALFOP) {
			$this->button->setColour(self::colour(self::HALFOP),true);
			return;		
		}
		else if($this->mode & self::VOICE) {
			$this->button->setColour(self::colour(self::VOICE),true);
			return;		
		} else {
			$this->button->setColour(self::colour(self::NORMAL),true);
			return;
		}

	}	
	
	static function getModeFromChar($char) {
		switch($char) {
			case('+'): { }
			case('v'): { return self::VOICE; }
			
			case('%'): { }
			case('h'): { return self::HALFOP; }
			
			case('@'): { }
			case('o'): { return self::OP; }
			
			case('&'): { }
			case('a'): { return self::ADMIN; }
			
			case('~'): { }
			case('q'): { return self::OWNER; }
			
			default: { return; }		
		}
	}
	
	static function getWordFromChar() {
		switch($char) {
			case('+'): { }
			case('v'): { return 'Voice'; }
			
			case('%'): { }
			case('h'): { return 'Half-Ops'; }
			
			case('@'): { }
			case('o'): { return 'Ops'; }
			
			case('+'): { }
			case('&'): { return 'Admin'; }
			
			case('~'): { }
			case('q'): { return 'Owner'; }			
		
			default: { return; }		
		}	
	}
	
	static function colour($mode) {
		if($mode & self::OWNER) {
			return commnet::$colour['nicklist-owner'];
		}
		if($mode & self::ADMIN) {
			return commnet::$colour['nicklist-admin'];
		}
		if($mode & self::OP) {
			return commnet::$colour['nicklist-op'];
		}
		if($mode & self::HALFOP) {
			return commnet::$colour['nicklist-halfop'];
		}
		if($mode & self::VOICE) {
			return commnet::$colour['nicklist-voice'];
		}
		else {
			return commnet::$colour['nicklist-normal'];
		}
	}

}

?>